﻿using System.Text;

using CSRedis;

using Tszy.Unlimited.Base.Infrastructure.Logging;
using Tszy.Unlimited.Base.Json;

namespace Tszy.Unlimited.Base.Infrastructure.Caching.Redis;

/// <summary>
/// CSRedis 组件缓存管理器。
/// </summary>
public class CSRedisCacheManager : ICacheManager
{
    private readonly ILogger _logger;

    /// <summary>
    /// 初始化 Redis 组件缓存管理器。
    /// </summary>
    /// <param name="connectionString">连接字符串。</param>
    public CSRedisCacheManager(string connectionString)
         : this(TszyLogger.Instance, connectionString)
    {
    }

    /// <summary>
    /// 初始化 Redis 组件缓存管理器。
    /// </summary>
    /// <param name="logger"></param>
    /// <param name="connectionString">连接字符串。</param>
    public CSRedisCacheManager(ILogger logger, string connectionString)
    {
        _logger = logger;

        var csredis = new CSRedisClient(connectionString);

        RedisHelper.Initialization(csredis);
    }

    /// <summary>
    /// 初始化 Redis 组件缓存管理器。
    /// </summary>
    /// <param name="host">主机地址。</param>
    /// <param name="port">端口号。</param>
    /// <param name="defaultDB">默认缓存库。</param>
    /// <param name="password">密码。</param>
    /// <param name="poolsize"></param>
    public CSRedisCacheManager(string host, int port, int defaultDB, string password = "", int poolsize = 50)
        : this(TszyLogger.Instance, host, port, defaultDB, password, poolsize)
    {
    }

    /// <summary>
    /// 初始化 Redis 组件缓存管理器。
    /// </summary>
    /// <param name="logger">日志记录器。</param>
    /// <param name="host">主机地址。</param>
    /// <param name="port">端口号。</param>
    /// <param name="defaultDB">默认缓存库。</param>
    /// <param name="password">密码。</param>
    /// <param name="poolsize"></param>
    public CSRedisCacheManager(ILogger logger, string host, int port, int defaultDB, string password = "", int poolsize = 50)
    {
        _logger = logger;

        string dbstring = $"{host}:{port},defaultDatabase={defaultDB},Password={password},poolsize={poolsize},ssl=false";

        var csredis = new CSRedisClient(dbstring);

        RedisHelper.Initialization(csredis);
    }

    /// <summary>
    /// 获得所有 Key。
    /// </summary>
    public IEnumerable<string> Keys
    {
        get
        {
            return RedisHelper.Keys("*");
        }
    }

    /// <summary>
    /// 增加缓存值。
    /// </summary>
    /// <typeparam name="TValue"></typeparam>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public bool Add<TValue>(string key, TValue value)
    {
        Set(key, value);
        return true;
    }

    /// <summary>
    /// 清空缓存。
    /// </summary>
    public void Clear()
    {
        var keys = Keys;

        if (keys == null)
        {
            return;
        }

        if (!keys.Any())
        {
            return;
        }

        foreach (var key in keys)
        {
            Remove(key);
        }
    }

    /// <summary>
    /// 获取缓存值。
    /// </summary>
    /// <typeparam name="TValue">值类型。</typeparam>
    /// <param name="key">键对象。</param>
    /// <returns>值对象。</returns>
    public TValue? Get<TValue>(string key)
    {
        return GetCache<TValue>(key);
    }

    /// <summary>
    /// 获取缓存值，如果不存在，则添加缓存。
    /// </summary>
    /// <typeparam name="TValue">值类型。</typeparam>
    /// <param name="key">键对象。</param>
    /// <param name="acquire">获得值对象的委托。</param>
    /// <returns>值对象。</returns>
    public TValue GetOrAdd<TValue>(string key, Func<TValue> acquire)
    {
        return GetOrAdd(key, acquire, TimeSpan.FromMinutes(20));
    }

    /// <summary>
    /// 获取缓存值，如果不存在，则添加缓存。
    /// </summary>
    /// <typeparam name="TValue">值类型。</typeparam>
    /// <param name="key">键对象。</param>
    /// <param name="acquire">获得值对象的委托。</param>
    /// <param name="validFor"></param>
    /// <returns>值对象。</returns>
    public TValue GetOrAdd<TValue>(string key, Func<TValue> acquire, TimeSpan validFor)
    {
        var result = Get<TValue>(key);

        if (result != null)
            return result;

        result = acquire();

        if (result != null)
            Set(key, result, validFor);

        return result;
    }

    /// <summary>
    /// 移除缓存值。
    /// </summary>
    /// <param name="key">键对象。</param>
    public bool Remove(string key)
    {
        RedisHelper.Del(key);

        return true;
    }

    /// <summary>
    /// 移除缓存值。
    /// </summary>
    /// <typeparam name="TValue">值类型。</typeparam>
    /// <param name="key">键对象。</param>
    /// <param name="result">值对象。</param>
    /// <returns></returns>
    public bool Remove<TValue>(string key, out TValue? result)
    {
        result = Get<TValue>(key);

        return Remove(key);
    }

    /// <summary>
    /// 增加缓存值。
    /// </summary>
    /// <typeparam name="TValue">值类型。</typeparam>
    /// <param name="key">键对象。</param>
    /// <param name="value">值对象。</param>
    public void Set<TValue>(string key, TValue value)
    {
        Set(key, value, TimeSpan.FromMinutes(20));
    }

    /// <summary>
    /// 增加缓存值。
    /// </summary>
    /// <remarks>
    /// 传入对象时，必须是可序列化才能保存成功。
    /// 假如对象 A 中包含着对象 B。
    /// 则对象 B 也必须要标记为可序列化才可以保存成功。
    /// 注意：保存到缓存中的实体，必须是带有默认构造函数的实体。
    /// </remarks>
    /// <typeparam name="TValue">值类型。</typeparam>
    /// <param name="key">键对象。</param>
    /// <param name="value">值对象。</param>
    /// <param name="validFor">有效时长。</param>
    public void Set<TValue>(string key, TValue value, TimeSpan validFor)
    {
        if (string.IsNullOrWhiteSpace(key))
            throw new ArgumentNullException(nameof(key), "键对象不可以是空的。");

        if (value == null)
            throw new ArgumentNullException(nameof(value), "值对象不可以是空的。");

        try
        {
            RedisHelper.Set(key, value, validFor);
        }
        catch (Exception exception)
        {
            var errorBuilder = new StringBuilder();

            errorBuilder.AppendLine("设置缓存数据时出现异常");
            errorBuilder.AppendLine($"KEY：{key}");
            errorBuilder.AppendLine($"Type:{typeof(TValue).FullName}");
            if (value != null)
            {
                errorBuilder.AppendLine($"缓存值：{JsonManager.Serialize(value)}");
            }
            errorBuilder.AppendLine($"错误信息：{exception.Message}");

            _logger.Fatal(errorBuilder.ToString(), exception);
        }
    }

    /// <summary>
    /// 增加缓存值。
    /// </summary>
    /// <typeparam name="TValue"></typeparam>
    /// <param name="key"></param>
    /// <param name="action"></param>
    /// <param name="timeOut"></param>
    /// <param name="lockTimeOut"></param>
    public void SetLock<TValue>(string key, Func<TValue, TValue> action, TimeSpan timeOut, TimeSpan lockTimeOut)
    {
        if (string.IsNullOrWhiteSpace(key))
            throw new ArgumentNullException(nameof(key), "键对象不可以是空的。");

        if (action == null)
            throw new ArgumentNullException(nameof(action), "设置方法不可以是空的。");

        var lockName = $"lock_task_action_{key}";

        using (RedisHelper.Lock(lockName, (int)lockTimeOut.TotalSeconds))
        {
            var value = Get<TValue>(key);

            if (value != null)
            {
                value = action(value);

                Set(key, value, timeOut);
            }
        }
    }

    /// <summary>
    /// 为给定 key 设置过期时间。
    /// </summary>
    /// <param name="key"></param>
    /// <param name="timeOut">时间间隔。</param>
    /// <returns></returns>
    public bool Expire(string key, TimeSpan timeOut)
    {
        return RedisHelper.Expire(key, timeOut);
    }

    /// <summary>
    /// 为给定 key 设置过期时间。
    /// </summary>
    /// <param name="key"></param>
    /// <param name="timeOut">过期时间。</param>
    /// <returns></returns>
    public bool ExpireAt(string key, DateTime timeOut)
    {
        return RedisHelper.ExpireAt(key, timeOut);
    }

    /// <summary>
    ///
    /// </summary>
    /// <remarks>
    /// 保存到缓存中的实体，必须是带有默认构造函数的实体。
    /// </remarks>
    /// <typeparam name="TValue"></typeparam>
    /// <param name="key"></param>
    /// <returns></returns>
    private TValue? GetCache<TValue>(string key)
    {
        try
        {
            return RedisHelper.Get<TValue>(key);
        }
        catch (Exception exception)
        {
            var errorBuilder = new StringBuilder();

            errorBuilder.AppendLine("获取缓存数据时出现异常");
            errorBuilder.AppendLine($"KEY：{key}");
            errorBuilder.AppendLine($"Type:{typeof(TValue).FullName}");
            errorBuilder.AppendLine($"错误信息：{exception.Message}");

            _logger.Fatal(errorBuilder.ToString(), exception);

            return default;
        }
    }

    #region 数值操作

    /// <summary>
    /// 根据指定的 Key，将值加1(仅整型有效)。
    /// </summary>
    /// <param name="key"></param>
    public void IncrementValue(string key)
    {
        RedisHelper.IncrBy(key);
    }

    /// <summary>
    /// 根据指定的Key，将值加上指定值(仅整型有效)。
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    public void IncrementValueBy(string key, int value)
    {
        RedisHelper.IncrBy(key, value);
    }

    /// <summary>
    /// 根据指定的 Key，将值减1(仅整型有效)。
    /// </summary>
    /// <param name="key"></param>
    public void DecrementValue(string key)
    {
        IncrementValueBy(key, -1);
    }

    /// <summary>
    /// 根据指定的 Key，将值减去指定值(仅整型有效)。
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    public void DecrementValueBy(string key, int value)
    {
        IncrementValueBy(key, -value);
    }

    #endregion 数值操作

    #region 集合操作

    /// <summary>
    /// 添加一个项到内部的 List。
    /// </summary>
    /// <param name="listId"></param>
    /// <param name="value"></param>
    public void AddItemToList(string listId, string value)
    {
        RedisHelper.SAdd(listId, value);
        //using (IRedisClient redisClient = GetRedisClient())
        //{
        //    redisClient.AddItemToList(listId, value);
        //}
    }

    /// <summary>
    /// 一次过将参数中的 List 中的多个值添加入内部的 List。
    /// </summary>
    /// <param name="listId"></param>
    /// <param name="value"></param>
    public void AddRangeToList(string listId, IEnumerable<string> value)
    {
        if (value == null || !value.Any())
        {
            return;
        }

        RedisHelper.SAdd(listId, value.ToArray());
        //using (IRedisClient redisClient = GetRedisClient())
        //{
        //    redisClient.AddRangeToList(listId, value.ToList());
        //}
    }

    /// <summary>
    /// 移除指定 ListId 的内部 List 中第二个参数值相等的那一项。
    /// </summary>
    /// <param name="listId"></param>
    /// <param name="value"></param>
    public void RemoveItemFromList(string listId, string value)
    {
        RedisHelper.SRem(listId, value);
        //using (IRedisClient redisClient = GetRedisClient())
        //{
        //    redisClient.RemoveItemFromList(listId, value);
        //}
    }

    /// <summary>
    /// 获取指定 ListId 的内部 List 的所有值。
    /// </summary>
    /// <param name="listId"></param>
    /// <returns></returns>
    public IEnumerable<string> GetAllItemsFromList(string listId)
    {
        return RedisHelper.Sort(listId);
        //using (IRedisClient redisClient = GetRedisClient())
        //{
        //    return redisClient.GetAllItemsFromList(listId);
        //}
    }

    #endregion 集合操作
}
